/* Include ----------------------------------------------*/
#include "SWC_OTA.h"
/* include core */
#include "main.h"
#include "cmsis_os.h"
#include "gpio.h"
#include "usart.h"
/* include BSP */
#include "w25qxx_Handler.h"
#include "AT24Cxx_Driver.h"
/* include Middlewares */
#include "FreeRTOS.h"
#include "task.h"
#include "semphr.h"
#include "queue.h"
#include "SEGGER_RTT.h"
#include "elog.h"
#include "ymodem.h"

/* Define -----------------------------------------------*/
#define UPPER_MACHINE_UART    (&huart1)   // 与上位机通信的串口

/* Variable ---------------------------------------------*/
/* state machine thread -------------*/
static E_Ota_State_t s_g_OTA_State = WAIT_REQ_DOWNLOAD;
static uint8_t s_u8_OTA_Cmd[4] = { 0 }; // Using IDLE irq receive cmd from upper computer
// debug
uint8_t W25Q_ReadBuf[4096] = {0};
uint16_t W25Q_ReadLength = 0;

/* tasks ----------------------------*/
/* OTA state machine */
QueueHandle_t Q_Ymodem_Rec_Len = NULL;  // Queue for Ymodem receive length
osThreadId_t ota_state_machine_thread_Handle;
const osThreadAttr_t ota_state_machine_thread_attributes = {
  .name = "ota_state_machine_thread",
  .stack_size = 128 * 4,
  .priority = (osPriority_t)osPriorityNormal1,
};
/* Download data from data buffer to external flash */
QueueHandle_t Q_AppDataBuffer = NULL;
SemaphoreHandle_t Semaphore_ExtFlashState = NULL;
osThreadId_t DownloadAppDataTaskHandle;
const osThreadAttr_t DownloadAppDataTaskattributes = {
  .name = "DownloadAppData_Task",
  .stack_size = 128 * 4,
  .priority = (osPriority_t)osPriorityNormal1,
};


/* Ymodem */
static int32_t s_g_file_size = 0;            // receive file size
static
uint8_t g_u8_YmodemRec_A[REC_MAX_NUM] = { 0 };      // receive data buffer
uint8_t g_u8_YmodemRec_B[REC_MAX_NUM] = { 0 };      // receive data buffer
extern int32_t packet_length;

/* Functions --------------------------------------------*/
static int8_t key_Scan(void);
void SoftReset(void);
void DownloadAppData_task(void* argument);


void ota_state_machine_thread(void* argument)
{
    /* Variable */
    uint8_t u8_rec_length = 0;
    uint8_t u8_Ackcmd[3] = { 0x44,0x55,0x66 };
    // create queue
    Q_Ymodem_Rec_Len = xQueueCreate(1, sizeof(uint16_t));

    uint8_t t_u8_OTAstate = 0x00;   // 0x00:没有APP更新
                                    // 0x11:APP数据下载过程中
                                    // 0x22:APP固件下载搬运完成，请求更新
    // 清除eeprom的OTA状态
    ee_WriteBytes(&t_u8_OTAstate, 0x00, 1); // 没有APP更新
    for (;;)
    {
        switch (s_g_OTA_State)
        {
        case WAIT_REQ_DOWNLOAD:
            // 阻塞式接收串口命令
            HAL_UARTEx_ReceiveToIdle_DMA(UPPER_MACHINE_UART, s_u8_OTA_Cmd, 4);
            xQueueReceive(Q_Ymodem_Rec_Len, &u8_rec_length, portMAX_DELAY);
            /* 校验数据长度 */
            if (3 == u8_rec_length)
            {
                /* 判断是否符合命令内容 */
                if (0x11 == s_u8_OTA_Cmd[0] && 0x22 == s_u8_OTA_Cmd[1] && 0x33 == s_u8_OTA_Cmd[2])
                {
                    // eeprom内写入新的OTA状态
                    t_u8_OTAstate = 0x11;
                    if(0 == ee_WriteBytes(&t_u8_OTAstate, 0x00, 1)) // APP数据下载过程中
                    {
                        log_e("error: ee_WriteBytes OTAstate failed!"); // error
                        continue;
                    }
                    /* 切换状态 */
                    s_g_OTA_State = OTA_DOWNLOADING;
                    /* 创建线程以及相关队列 */
                    // 创建队列
                    Q_AppDataBuffer = xQueueCreate(2, sizeof(uint8_t*));
                    // 创建互斥锁
                    Semaphore_ExtFlashState = xSemaphoreCreateMutex();
					// 创建线程
                    DownloadAppDataTaskHandle = osThreadNew(DownloadAppData_task, NULL, &DownloadAppDataTaskattributes);
                }
                else
                {
                    for (uint8_t i = 0;i < 4;i++) {
                        /* 清掉数据 */
                        s_u8_OTA_Cmd[i] = 0;
                    }
                }
            }
            else
            {
                for (uint8_t i = 0;i < 4;i++) {
                    /* 清掉数据 */
                    s_u8_OTA_Cmd[i] = 0;
                }
            }
            break;
        case OTA_DOWNLOADING:
            /* 进入固件下载，并校验下载状态 */
            s_g_file_size = Ymodem_Receive(g_u8_YmodemRec_A, g_u8_YmodemRec_B);
            if (s_g_file_size > 0)
            {
                log_i("file_size = [%d]", s_g_file_size);
                /* 切换状态，发送应答告诉上位机数据下载完毕 */
                s_g_OTA_State = WAIT_REQ_UPDATE;
                HAL_UART_Transmit(UPPER_MACHINE_UART, u8_Ackcmd, 3, 1000);
                
				// 拿互斥锁，保证w25q已经写入完成
				xSemaphoreTake(Semaphore_ExtFlashState, portMAX_DELAY);
                xSemaphoreGive(Semaphore_ExtFlashState);
                /* 最后将数据缓冲区中剩余的数据写入 */
                W25Q64_WriteData_End();

                /* 清除一切生成的资源（队列、线程），后面用不到了 */
                // 删除线程
                vTaskDelete(DownloadAppDataTaskHandle);
                // 删除队列
                vQueueDelete(Q_AppDataBuffer);
                // 删除互斥量
                vSemaphoreDelete(Semaphore_ExtFlashState);
				
				// debug
				// if(0 != W25Q64_ReadData(W25Q_ReadBuf, &W25Q_ReadLength))
				// {
				// 	log_e("error: W25Q64_ReadData failed!");
				// }

                // eeprom内写入新的OTA状态
                t_u8_OTAstate = 0x22;
                if (0 == ee_WriteBytes(&t_u8_OTAstate, 0x00, 1)) // APP固件下载完成，请求更新
                {
                    log_e("error: ee_WriteBytes OTAstate failed!");
                }
				else
				{
					log_d("ee_WriteBytes 0x22 success.");
				}    
				
                // eeprom内写入文件大小
                if (0 == ee_WriteBytes((uint8_t*)(&s_g_file_size), 0x01, 4)) // 写入4字节的文件大小
                {
                    log_e("error: ee_WriteBytes new App_size failed!");
                }
				else
				{
					log_d("ee_WriteBytes file_size success.");
				}
                
#if 0	// test eeprom
				osDelay(1);
                uint8_t t_u8_readstate = 0;
                int32_t t_32_filesize = 0;
                if (1 == ee_ReadBytes(&t_u8_readstate, 0x00, 1)) {
                    //log_i("ee_ReadBytes success: OTAstate = [%x]", t_u8_readstate);
					HAL_UART_Transmit(&huart1,&t_u8_readstate,1,1000);
                }
                if (1 == ee_ReadBytes((uint8_t*)(&t_32_filesize), 0x01, 4)) {
                    //log_i("ee_ReadBytes success: new filesize = [%d]", t_32_filesize);
					HAL_UART_Transmit(&huart1,(uint8_t*)(&t_32_filesize),4,1000);
                }
#endif  // test eeprom

            }
            else
            {
                log_e("error: Ymodem_Receive failed! file_size = [%d]", s_g_file_size);
                s_g_file_size = 0;
                /* 切换状态，清除一切生成的资源（队列、线程） */
                s_g_OTA_State = WAIT_REQ_DOWNLOAD;
                // 清除eeprom的OTA状态
                ee_WriteBytes(&t_u8_OTAstate, 0x00, 1); // 没有APP更新
                // 删除线程
                vTaskDelete(DownloadAppDataTaskHandle);
                // 删除队列
                vQueueDelete(Q_AppDataBuffer);
                // 删除互斥量
                vSemaphoreDelete(Semaphore_ExtFlashState);
            }
            break;
        case WAIT_REQ_UPDATE:
            // 阻塞式接收串口命令
            HAL_UARTEx_ReceiveToIdle_DMA(UPPER_MACHINE_UART, s_u8_OTA_Cmd, 4);
            xQueueReceive(Q_Ymodem_Rec_Len, &u8_rec_length, portMAX_DELAY);
            /* 校验数据长度 */
            if (3 == u8_rec_length)
            {
                /* 判断是否符合命令内容 */
                if (0x77 == s_u8_OTA_Cmd[0] && 0x88 == s_u8_OTA_Cmd[1] && 0x99 == s_u8_OTA_Cmd[2])
                {
                    /* 切换状态 */
                    s_g_OTA_State = OTA_END;
                }
                else
                {
                    for (uint8_t i = 0;i < 4;i++) {
                        /* 清掉数据 */
                        s_u8_OTA_Cmd[i] = 0;
                    }
                }
            }
            else
            {
                for (uint8_t i = 0;i < 4;i++) {
                    /* 清掉数据 */
                    s_u8_OTA_Cmd[i] = 0;
                }
            }
            break;
        case OTA_END:
            if (1 == key_Scan())
            {
                /* 执行软复位 */
                SoftReset();
            }
            else
            {
                /* 切换状态，如果重启则会进入bootloader升级 */
                s_g_OTA_State = WAIT_REQ_DOWNLOAD;
            }
            break;
        
        default:
            break;
        }
    }
}

uint32_t g_u32_datalength = 0;
/**
 * @brief   DownloadAppData_task thread
 *
 * 功能说明：将接收到的APP数据搬运到外部flash中
 *
 * @param[in]   argument：任务参数
 * @retval  none
 */
void DownloadAppData_task(void* argument)
{
    uint8_t* pu8_buffer = NULL;
    int32_t* p32_size = NULL;
    if (NULL == Q_AppDataBuffer)
    {
        log_e("error: Queue Q_AppDataBuffer is NULL!");
        while (NULL == Q_AppDataBuffer);
    }
    // 第一包数据接收的肯定是size
    xQueueReceive(Q_AppDataBuffer, &p32_size, portMAX_DELAY);
    // 需要擦除掉flash size对应的所有扇区
    // 此处不一次擦除所有涉及扇区，太费时，选择在W25Q64_WriteData函数中逐步擦除

    // 释放互斥量
    xSemaphoreGive(Semaphore_ExtFlashState);
    for (;;)
    {
        // 后续的数据包发送的队列消息就是buffer地址
        xQueueReceive(Q_AppDataBuffer, &pu8_buffer, portMAX_DELAY);
        // 通过互斥量锁住buffer
        xSemaphoreTake(Semaphore_ExtFlashState, portMAX_DELAY);
        if (pu8_buffer == NULL)
        {
            continue;
        }
        // 执行写入W25q的逻辑
        // W25Q64_WriteData(pu8_buffer, packet_length); // 原有代码
        W25Q64_WriteData(pu8_buffer, g_u32_datalength); // 更正后的代码
        xSemaphoreGive(Semaphore_ExtFlashState);
    }
}

/**
  * @brief  Reception Event Callback (Rx event notification called after use of advanced reception service).
  * @param  huart UART handle
  * @param  Size  Number of data available in application reception buffer (indicates a position in
  *               reception buffer until which, data are available)
  * @retval None
  */
void HAL_UARTEx_RxEventCallback(UART_HandleTypeDef* huart, uint16_t Size)
{
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;
    // 判断是串口1
    if (huart->Instance == USART1)
    {
        // 确定是串口空闲中断触发的回调函数，获取已经传输值
        if (1 == __HAL_UART_GET_FLAG(huart, UART_FLAG_IDLE))
        {
            // 通过队列将接收长度发送到 Q_Ymodem_Rec_Len 队列
            if (pdTRUE == xQueueSendFromISR(Q_Ymodem_Rec_Len, &Size, 0))
            {
                xHigherPriorityTaskWoken = pdTRUE;  // 发送完毕后我们进行手动切换任务
            }
            HAL_UART_DMAStop(huart);
        }
        // DMA传输完成后的操作
        if (REC_MAX_NUM == Size)  // DMA全满中断
        {
            // 使用空闲中断完成任务，不应该进入全满中断
            log_e("error: DMA_Cplt_Callback!");
        }

        // 触发任务切换
        portYIELD_FROM_ISR(xHigherPriorityTaskWoken);
    }
}

/**
 * @brief   Scan key press
 *
 * 功能说明：检测按键20s内是否被按下
 *
 * @retval  1:  key has been pressed
 *          -1: time out, key has not been pressed
 */
static int8_t key_Scan()
{
    uint16_t Key_Scan_TimeCnt = 0;
    // 检测20s，20s内是否按下按键
    for (Key_Scan_TimeCnt = 0;Key_Scan_TimeCnt < 400;Key_Scan_TimeCnt++)
    {
        if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port, Key_Pin))
        {
            osDelay(20);
            if (GPIO_PIN_RESET == HAL_GPIO_ReadPin(Key_GPIO_Port, Key_Pin))
            {
                /* 判断为按下 */
                return 1;
            }
        }
        osDelay(50);
    }
    /* 判断为超时 */
    return -1;
}

/**
 * @brief   System SoftReset
 *
 * 功能说明：系统软复位
 *
 * @retval  None
 */
void SoftReset(void)
{
	// 屏蔽所有可屏蔽中断
    __set_FAULTMASK(1);
	// 系统软复位
    NVIC_SystemReset();
}
